package com.xueyi.workflow.util;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.xueyi.workflow.domain.dto.GraphSchema;
import com.xueyi.workflow.domain.dto.GraphSchema.FlowNode;
import com.xueyi.workflow.domain.dto.GraphSchema.FlowEdge;
import java.util.*;

/**
 * VueFlow 流程设计器的 BPMN XML 生成器
 * 专门处理 VueFlow 格式的数据，包括连线的条件表达式等字段
 * 
 * @author xueyi
 */
public class VueFlowBpmnGenerator extends AbstractBpmnGenerator {
    
    private final ObjectMapper objectMapper = new ObjectMapper();
    
    @Override
    public String generateBpmnXml(GraphSchema schema, String processId, String processName) {
        // 对于标准的 GraphSchema，使用原有的生成逻辑
        return LegacyBpmnGenerator.generateBpmnXml(schema, processId, processName);
    }
    
    @Override
    public String generateBpmnXmlFromRawData(Object rawData, String processId, String processName) {
        try {
            // 将原始数据转换为 Map，保留所有字段
            Map<String, Object> schemaMap;
            if (rawData instanceof String) {
                schemaMap = objectMapper.readValue((String) rawData, Map.class);
            } else if (rawData instanceof Map) {
                schemaMap = (Map<String, Object>) rawData;
            } else {
                throw new IllegalArgumentException("不支持的数据类型: " + rawData.getClass().getSimpleName());
            }
            
            StringBuilder bpmn = new StringBuilder();
            
            // 1. 生成 XML 头部
            bpmn.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
            bpmn.append("<definitions xmlns=\"http://www.omg.org/spec/BPMN/20100524/MODEL\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:flowable=\"http://flowable.org/bpmn\" targetNamespace=\"http://www.flowable.org/processdef\">\n");
            
            // 2. 开始流程定义
            bpmn.append("  <process id=\"").append(processId).append("\" name=\"").append(processName).append("\" isExecutable=\"true\">\n");

            // 3. 生成节点
            List<Map<String, Object>> nodes = (List<Map<String, Object>>) schemaMap.get("nodes");
            if (nodes != null) {
                for (Map<String, Object> node : nodes) {
                    generateNodeXmlFromMap(bpmn, node);
                }
            }
            
            // 4. 生成连线
            List<Map<String, Object>> edges = (List<Map<String, Object>>) schemaMap.get("edges");
            if (edges != null) {
                for (Map<String, Object> edge : edges) {
                    generateEdgeXmlFromMap(bpmn, edge);
                }
            }
            
            // 5. 结束流程定义
            bpmn.append("  </process>\n</definitions>\n");
            return bpmn.toString();
            
        } catch (Exception e) {
            throw new RuntimeException("生成 BPMN XML 失败: " + e.getMessage(), e);
        }
    }
    
    @Override
    public boolean supports(Object rawData) {
        if (rawData instanceof String) {
            try {
                Map<String, Object> map = objectMapper.readValue((String) rawData, Map.class);
                return map.containsKey("nodes") && map.containsKey("edges");
            } catch (Exception e) {
                return false;
            }
        } else if (rawData instanceof Map) {
            Map<String, Object> map = (Map<String, Object>) rawData;
            return map.containsKey("nodes") && map.containsKey("edges");
        }
        return false;
    }
    
    @Override
    public GraphSchema convertToGraphSchema(Object rawData) {
        try {
            // 将 VueFlow 数据转换为标准的 GraphSchema
            Map<String, Object> schemaMap;
            if (rawData instanceof String) {
                schemaMap = objectMapper.readValue((String) rawData, Map.class);
            } else if (rawData instanceof Map) {
                schemaMap = (Map<String, Object>) rawData;
            } else {
                throw new IllegalArgumentException("不支持的数据类型: " + rawData.getClass().getSimpleName());
            }
            
            GraphSchema schema = new GraphSchema();
            
            // 转换节点
            List<Map<String, Object>> nodes = (List<Map<String, Object>>) schemaMap.get("nodes");
            if (nodes != null) {
                List<FlowNode> flowNodes = new ArrayList<>();
                for (Map<String, Object> node : nodes) {
                    flowNodes.add(convertNodeToFlowNode(node));
                }
                schema.setNodes(flowNodes);
            }
            
            // 转换连线
            List<Map<String, Object>> edges = (List<Map<String, Object>>) schemaMap.get("edges");
            if (edges != null) {
                List<FlowEdge> flowEdges = new ArrayList<>();
                for (Map<String, Object> edge : edges) {
                    flowEdges.add(convertEdgeToFlowEdge(edge));
                }
                schema.setEdges(flowEdges);
            }
            
            return schema;
            
        } catch (Exception e) {
            throw new RuntimeException("转换为 GraphSchema 失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 将 VueFlow 节点转换为标准 FlowNode
     */
    private FlowNode convertNodeToFlowNode(Map<String, Object> node) {
        FlowNode flowNode = new FlowNode();
        flowNode.setId((String) node.get("id"));
        flowNode.setType((String) node.get("type"));
        
        // 转换 data 字段
        Map<String, Object> nodeData = (Map<String, Object>) node.get("data");
        if (nodeData != null) {
            // 这里可以根据需要扩展，将 VueFlow 的 data 字段映射到 GraphSchema 的相应字段
            // 暂时保持原有的数据结构
            flowNode.setData(nodeData);
        }
        
        return flowNode;
    }
    
    /**
     * 将 VueFlow 连线转换为标准 FlowEdge
     * 注意：这里会丢失条件表达式等字段，因为标准 FlowEdge 只包含 source 和 target
     */
    private FlowEdge convertEdgeToFlowEdge(Map<String, Object> edge) {
        FlowEdge flowEdge = new FlowEdge();
        flowEdge.setSource((String) edge.get("source"));
        flowEdge.setTarget((String) edge.get("target"));
        
        // 注意：VueFlow 的条件表达式等字段在这里会丢失
        // 如果需要保留这些字段，需要扩展 GraphSchema.FlowEdge 类
        // 或者使用自定义的扩展字段
        
        return flowEdge;
    }
    
    // 以下是具体的 XML 生成方法，从原有的 BpmnGenerator 复制过来
    
    /**
     * 从 Map 生成节点 XML
     */
    private void generateNodeXmlFromMap(StringBuilder bpmn, Map<String, Object> node) {
        String nodeType = (String) node.get("type");
        String nodeId = (String) node.get("id");
        Map<String, Object> nodeData = (Map<String, Object>) node.get("data");
        
        if (nodeData == null) {
            nodeData = new HashMap<>();
        }
        
        switch (nodeType) {
            case "start":
            case "input":
            case "event":
                generateEventNodeXmlFromMap(bpmn, nodeId, nodeData);
                break;
            case "end":
            case "output":
                generateEndEventXmlFromMap(bpmn, nodeId, nodeData);
                break;
            case "userTask":
            case "default":
                generateUserTaskXmlFromMap(bpmn, nodeId, nodeData);
                break;
            case "serviceTask":
            case "task":
                generateServiceTaskXmlFromMap(bpmn, nodeId, nodeData);
                break;
            case "scriptTask":
                generateScriptTaskXmlFromMap(bpmn, nodeId, nodeData);
                break;
            case "condition":
            case "exclusiveGateway":
                generateExclusiveGatewayXmlFromMap(bpmn, nodeId, nodeData);
                break;
            case "parallelGateway":
                generateParallelGatewayXmlFromMap(bpmn, nodeId, nodeData);
                break;
            case "eventBasedGateway":
                generateEventBasedGatewayXmlFromMap(bpmn, nodeId, nodeData);
                break;
            case "gateway":
                generateGatewayXmlFromMap(bpmn, nodeId, nodeData);
                break;
            case "boundaryEvent":
                generateBoundaryEventXmlFromMap(bpmn, nodeId, nodeData);
                break;
            case "cc":
                generateCcNodeXmlFromMap(bpmn, nodeId, nodeData);
                break;
            case "subProcess":
                generateSubProcessXmlFromMap(bpmn, nodeId, nodeData);
                break;
        }
    }
    
    /**
     * 从 Map 生成连线 XML
     * 核心逻辑：直接从 Map 中读取连线的所有字段，包括条件表达式
     */
    private void generateEdgeXmlFromMap(StringBuilder bpmn, Map<String, Object> edge) {
        String source = (String) edge.get("source");
        String target = (String) edge.get("target");
        
        // 1. 生成连线基础信息
        bpmn.append("    <sequenceFlow id=\"").append(source).append("_to_").append(target)
            .append("\" sourceRef=\"").append(source).append("\" targetRef=\"").append(target).append("\"");
        
        // 2. 检查连线是否包含条件表达式
        // 直接从 Map 中读取，保留所有字段
        String conditionType = (String) edge.get("conditionType");
        String conditionExpression = (String) edge.get("conditionExpression");
        String scriptContent = (String) edge.get("scriptContent");
        String scriptLanguage = (String) edge.get("scriptLanguage");
        
        // 3. 处理条件表达式
        if (conditionType != null && !conditionType.trim().isEmpty()) {
            if ("expression".equals(conditionType) && conditionExpression != null && !conditionExpression.trim().isEmpty()) {
                bpmn.append(">\n      <conditionExpression xsi:type=\"tFormalExpression\">")
                    .append(conditionExpression)
                    .append("</conditionExpression>\n    </sequenceFlow>\n");
                return;
            } else if ("script".equals(conditionType) && scriptContent != null && !scriptContent.trim().isEmpty()) {
                String language = scriptLanguage != null ? scriptLanguage : "javascript";
                bpmn.append(">\n      <conditionExpression xsi:type=\"tFormalExpression\" language=\"").append(language).append("\">")
                    .append(scriptContent)
                    .append("</conditionExpression>\n    </sequenceFlow>\n");
                return;
            }
        }
        
        // 4. 检查 data 字段中的条件配置
        Object edgeData = edge.get("data");
        if (edgeData instanceof Map) {
            Map<String, Object> dataMap = (Map<String, Object>) edgeData;
            String dataConditionType = (String) dataMap.get("conditionType");
            if (dataConditionType != null && !dataConditionType.trim().isEmpty()) {
                if ("expression".equals(dataConditionType)) {
                    String dataConditionExpression = (String) dataMap.get("conditionExpression");
                    if (dataConditionExpression != null && !dataConditionExpression.trim().isEmpty()) {
                        bpmn.append(">\n      <conditionExpression xsi:type=\"tFormalExpression\">")
                            .append(dataConditionExpression)
                            .append("</conditionExpression>\n    </sequenceFlow>\n");
                        return;
                    }
                } else if ("script".equals(dataConditionType)) {
                    String dataScriptLanguage = (String) dataMap.getOrDefault("scriptLanguage", "javascript");
                    String dataScriptContent = (String) dataMap.get("scriptContent");
                    if (dataScriptContent != null && !dataScriptContent.trim().isEmpty()) {
                        bpmn.append(">\n      <conditionExpression xsi:type=\"tFormalExpression\" language=\"").append(dataScriptLanguage).append("\">")
                            .append(dataScriptContent)
                            .append("</conditionExpression>\n    </sequenceFlow>\n");
                        return;
                    }
                }
            }
        }
        
        // 5. 如果没有找到条件，生成无条件连线
        bpmn.append(" />\n");
    }
    
    // 其他节点生成方法（从原有的 BpmnGenerator 复制）
    // 为了保持代码简洁，这里省略了其他方法的实现
    // 实际使用时需要完整实现所有方法
    
    private void generateEventNodeXmlFromMap(StringBuilder bpmn, String nodeId, Map<String, Object> nodeData) {
        String eventType = (String) nodeData.getOrDefault("eventType", "start");
        if ("start".equals(eventType)) {
            bpmn.append("    <startEvent id=\"").append(nodeId).append("\" name=\"").append(nodeData.getOrDefault("label", "开始")).append("\" />\n");
        } else if ("end".equals(eventType)) {
            bpmn.append("    <endEvent id=\"").append(nodeId).append("\" name=\"").append(nodeData.getOrDefault("label", "结束")).append("\" />\n");
        } else {
            bpmn.append("    <startEvent id=\"").append(nodeId).append("\" name=\"").append(nodeData.getOrDefault("label", "开始")).append("\" />\n");
        }
    }
    
    private void generateEndEventXmlFromMap(StringBuilder bpmn, String nodeId, Map<String, Object> nodeData) {
        bpmn.append("    <endEvent id=\"").append(nodeId).append("\" name=\"").append(nodeData.getOrDefault("label", "结束")).append("\" />\n");
    }
    
    private void generateUserTaskXmlFromMap(StringBuilder bpmn, String nodeId, Map<String, Object> nodeData) {
        bpmn.append("    <userTask id=\"").append(nodeId).append("\" name=\"").append(nodeData.getOrDefault("label", "用户任务")).append("\"");
        
        // 处理人员配置
        Object assignees = nodeData.get("assignees");
        if (assignees instanceof List && !((List<?>)assignees).isEmpty()) {
            List<Map<String, Object>> assigneeList = (List<Map<String, Object>>) assignees;
            
            // 收集不同类型的处理人
            List<String> users = new ArrayList<>();
            String assignee = null;
            
            for (Map<String, Object> assigneeMap : assigneeList) {
                String type = (String) assigneeMap.get("type");
                Object value = assigneeMap.get("value");
                
                if ("user".equals(type) && value instanceof List) {
                    List<?> userList = (List<?>) value;
                    for (Object user : userList) {
                        if (user instanceof Map) {
                            String userId = String.valueOf(((Map<?, ?>) user).get("id"));
                            users.add(userId);
                            if (assignee == null && userList.size() == 1) {
                                assignee = userId;
                            }
                        }
                    }
                }
            }
            
            // 设置assignee
            if (assignee != null) {
                bpmn.append(" flowable:assignee=\"").append(assignee).append("\"");
            }
            
            // 设置candidateUsers
            if (!users.isEmpty()) {
                bpmn.append(" flowable:candidateUsers=\"").append(String.join(",", users)).append("\"");
            }
        }
        
        bpmn.append(">");
        
        // 添加自定义任务监听器
        bpmn.append("<extensionElements>");
        bpmn.append("<flowable:taskListener event=\"create\" class=\"com.xueyi.workflow.listener.CustomTaskListener\" />");
        
        // 抄送配置
        Object cc = nodeData.get("cc");
        Object notifyMethods = nodeData.get("notifyMethods");
        if (cc != null) {
            bpmn.append("<flowable:field name=\"cc\" stringValue=\"")
                .append(cc.toString().replaceAll("\"", "'"))
                .append("\" />");
        }
        if (notifyMethods != null) {
            bpmn.append("<flowable:field name=\"notifyMethods\" stringValue=\"")
                .append(notifyMethods.toString().replaceAll("\"", "'"))
                .append("\" />");
        }
        
        bpmn.append("</extensionElements>");
        bpmn.append("</userTask>\n");
    }
    
    private void generateServiceTaskXmlFromMap(StringBuilder bpmn, String nodeId, Map<String, Object> nodeData) {
        bpmn.append("    <serviceTask id=\"").append(nodeId).append("\" name=\"").append(nodeData.getOrDefault("label", "服务任务")).append("\" />\n");
    }
    
    private void generateScriptTaskXmlFromMap(StringBuilder bpmn, String nodeId, Map<String, Object> nodeData) {
        bpmn.append("    <scriptTask id=\"").append(nodeId).append("\" name=\"").append(nodeData.getOrDefault("label", "脚本任务")).append("\" scriptFormat=\"groovy\">");
        Object script = nodeData.get("script");
        if (script != null) bpmn.append(script);
        bpmn.append("</scriptTask>\n");
    }
    
    private void generateExclusiveGatewayXmlFromMap(StringBuilder bpmn, String nodeId, Map<String, Object> nodeData) {
        bpmn.append("    <exclusiveGateway id=\"").append(nodeId).append("\" name=\"").append(nodeData.getOrDefault("label", "条件网关")).append("\" />\n");
    }
    
    private void generateParallelGatewayXmlFromMap(StringBuilder bpmn, String nodeId, Map<String, Object> nodeData) {
        bpmn.append("    <parallelGateway id=\"").append(nodeId).append("\" name=\"").append(nodeData.getOrDefault("label", "并行网关")).append("\" />\n");
    }
    
    private void generateEventBasedGatewayXmlFromMap(StringBuilder bpmn, String nodeId, Map<String, Object> nodeData) {
        bpmn.append("    <eventBasedGateway id=\"").append(nodeId).append("\" name=\"").append(nodeData.getOrDefault("label", "事件网关")).append("\" />\n");
    }
    
    private void generateGatewayXmlFromMap(StringBuilder bpmn, String nodeId, Map<String, Object> nodeData) {
        String gatewayType = (String) nodeData.getOrDefault("gatewayType", "exclusive");
        String gatewayName = (String) nodeData.getOrDefault("label", "网关");
        switch (gatewayType) {
            case "exclusive":
                bpmn.append("    <exclusiveGateway id=\"").append(nodeId).append("\" name=\"").append(gatewayName).append("\" />\n");
                break;
            case "parallel":
                bpmn.append("    <exclusiveGateway id=\"").append(nodeId).append("\" name=\"").append(gatewayName).append("\" />\n");
                break;
            default:
                bpmn.append("    <exclusiveGateway id=\"").append(nodeId).append("\" name=\"").append(gatewayName).append("\" />\n");
                break;
        }
    }
    
    private void generateBoundaryEventXmlFromMap(StringBuilder bpmn, String nodeId, Map<String, Object> nodeData) {
        bpmn.append("    <boundaryEvent id=\"").append(nodeId).append("\" attachedToRef=\"").append(nodeData.getOrDefault("attachedTo", "")).append("\">");
        bpmn.append("</boundaryEvent>\n");
    }
    
    private void generateCcNodeXmlFromMap(StringBuilder bpmn, String nodeId, Map<String, Object> nodeData) {
        bpmn.append("    <serviceTask id=\"").append(nodeId).append("\" name=\"").append(nodeData.getOrDefault("label", "抄送")).append("\" />\n");
    }
    
    private void generateSubProcessXmlFromMap(StringBuilder bpmn, String nodeId, Map<String, Object> nodeData) {
        bpmn.append("    <subProcess id=\"").append(nodeId).append("\" name=\"").append(nodeData.getOrDefault("label", "子流程")).append("\">\n");
        bpmn.append("    </subProcess>\n");
    }
}
